/**************************************************************************************************
* Title: RenderManager.cpp
* Authors: Gael Huber, Erin Mooney
* Description: Manages OpenGL rendering pipeline.
**************************************************************************************************/
#include "SmurfsRenderManager.h"

// Singleton instance
template<> RenderManager* Singleton<RenderManager>::singleton = 0;

/**************************************************************************************************
* Constructor
**************************************************************************************************/
RenderManager::RenderManager(void) {
	// Do nothing
}

/**************************************************************************************************
* Destructor
**************************************************************************************************/
RenderManager::~RenderManager(void) {
	// Do nothing
}

/**************************************************************************************************
* Initialize OpenGL
**************************************************************************************************/
void RenderManager::startUp(void) {
	// Initialize a new window
	_window = new Window();
	_window->startUp(L"SMURFS Engine", 640, 480, 16);

	glShadeModel(GL_SMOOTH);	// Enables smooth shading
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);	// Set a black background
	glClearDepth(1.0f);		// Depth buffer setup
	glEnable(GL_DEPTH_TEST);	// Enable depth testing
	glDepthFunc(GL_LEQUAL);	// The type of depth test
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);			// Really Nice Perspective Calculations

	// Build the display lists
	//buildDisplayLists();

	// Build the font
	//buildFont();
}

/**************************************************************************************************
* Shutdown
**************************************************************************************************/
void RenderManager::shutDown(void) {
	killFont();	// Destroy the font used for text rendering

	// Destroy the window
	_window->shutDown();
	delete _window;
}

/**************************************************************************************************
* Get the singleton instance
**************************************************************************************************/
RenderManager& RenderManager::getSingleton(void) { 
	assert(singleton);
	return *singleton;
}

/**************************************************************************************************
* Get the singleton pointer
**************************************************************************************************/
RenderManager* RenderManager::getSingletonPtr(void) {
	return singleton;
}

/**************************************************************************************************
* Build the display lists
**************************************************************************************************/
void RenderManager::buildDisplayLists(void) {
	// Build a pyramid
	Mesh obj;
	obj.data = glGenLists(1);
	glNewList(obj.data, GL_COMPILE);	// New compiled object
		glBegin(GL_TRIANGLES);				// Start Drawing The Pyramid
			glColor3f(0.0f,1.0f,0.0f);			// Set The Color To Green
			glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Front)
			glVertex3f(-0.25f,-0.25f, 0.25f);		// Left Of Triangle (Front)
			glVertex3f( 0.25f,-0.25f, 0.25f);		// Right Of Triangle (Front)

			glColor3f(1.0f,0.5f,0.0f);			// Set The Color To Orange
			glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Right)
			glVertex3f( 0.25f,-0.25f, 0.25f);		// Left Of Triangle (Right)
			glVertex3f( 0.25f,-0.25f, -0.25f);		// Right Of Triangle (Right)

			glColor3f(0.0f,0.0f,1.0f);			// Set The Color To Blue
			glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Back)
			glVertex3f( 0.25f,-0.25f, -0.25f);		// Left Of Triangle (Back)
			glVertex3f(-0.25f,-0.25f, -0.25f);		// Right Of Triangle (Back)

			glColor3f(1.0f,0.0f,1.0f);			// Set The Color To Violet
			glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Left)
			glVertex3f(-0.25f,-0.25f,-0.25f);		// Left Of Triangle (Left)
			glVertex3f(-0.25f,-0.25f, 0.25f);		// Right Of Triangle (Left)
		glEnd();							// Done Drawing The Pyramid
	glEndList();
	objectList.push_back(obj);

	// Build a cube
	obj.data = glGenLists(1);
	glNewList(obj.data, GL_COMPILE);	// New compiled object
		glBegin(GL_QUADS);					// Start Drawing The Cube
			glColor3f(0.0f,1.0f,0.0f);			// Set The Color To Green
			glVertex3f( 0.25f, 0.25f,-0.25f);			// Top Right Of The Quad (Top)
			glVertex3f(-0.25f, 0.25f,-0.25f);			// Top Left Of The Quad (Top)
			glVertex3f(-0.25f, 0.25f, 0.25f);			// Bottom Left Of The Quad (Top)
			glVertex3f( 0.25f, 0.25f, 0.25f);			// Bottom Right Of The Quad (Top)

			glColor3f(1.0f,0.5f,0.0f);			// Set The Color To Orange
			glVertex3f( 0.25f,-0.25f, 0.25f);			// Top Right Of The Quad (Bottom)
			glVertex3f(-0.25f,-0.25f, 0.25f);			// Top Left Of The Quad (Bottom)
			glVertex3f(-0.25f,-0.25f,-0.25f);			// Bottom Left Of The Quad (Bottom)
			glVertex3f( 0.25f,-0.25f,-0.25f);			// Bottom Right Of The Quad (Bottom)

			glColor3f(1.0f,0.0f,0.0f);			// Set The Color To Red
			glVertex3f( 0.25f, 0.25f, 0.25f);			// Top Right Of The Quad (Front)
			glVertex3f(-0.25f, 0.25f, 0.25f);			// Top Left Of The Quad (Front)
			glVertex3f(-0.25f,-0.25f, 0.25f);			// Bottom Left Of The Quad (Front)
			glVertex3f( 0.25f,-0.25f, 0.25f);			// Bottom Right Of The Quad (Front)

			glColor3f(1.0f,1.0f,0.0f);			// Set The Color To Yellow
			glVertex3f( 0.25f,-0.25f,-0.25f);			// Bottom Left Of The Quad (Back)
			glVertex3f(-0.25f,-0.25f,-0.25f);			// Bottom Right Of The Quad (Back)
			glVertex3f(-0.25f, 0.25f,-0.25f);			// Top Right Of The Quad (Back)
			glVertex3f( 0.25f, 0.25f,-0.25f);			// Top Left Of The Quad (Back)

			glColor3f(0.0f,0.0f,1.0f);			// Set The Color To Blue
			glVertex3f(-0.25f, 0.25f, 0.25f);			// Top Right Of The Quad (Left)
			glVertex3f(-0.25f, 0.25f,-0.25f);			// Top Left Of The Quad (Left)
			glVertex3f(-0.25f,-0.25f,-0.25f);			// Bottom Left Of The Quad (Left)
			glVertex3f(-0.25f,-0.25f, 0.25f);			// Bottom Right Of The Quad (Left)

			glColor3f(1.0f,0.0f,1.0f);			// Set The Color To Violet
			glVertex3f( 0.25f, 0.25f,-0.25f);			// Top Right Of The Quad (Right)
			glVertex3f( 0.25f, 0.25f, 0.25f);			// Top Left Of The Quad (Right)
			glVertex3f( 0.25f,-0.25f, 0.25f);			// Bottom Left Of The Quad (Right)
			glVertex3f( 0.25f,-0.25f,-0.25f);			// Bottom Right Of The Quad (Right)
		glEnd();						// Done Drawing The Quad
	glEndList();
	objectList.push_back(obj);
}

/**************************************************************************************************
* Render the scene
**************************************************************************************************/
void RenderManager::renderScene(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear the screen

	// translate and rotate the pyramid
	/*glLoadIdentity();
	glTranslatef(-1.5f,0.0f,-6.0f);				// Move Left And Into The Screen
	//glRotatef(rtri,0.0f,1.0f,1.0f);				// Rotate The Pyramid On It's Y Axis

	// pyramid
	glBegin(GL_TRIANGLES);					// Start Drawing The Pyramid
		glColor3f(1.0f,0.0f,0.0f);			// Red
		glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Front)
		glColor3f(0.0f,1.0f,0.0f);			// Green
		glVertex3f(-0.25f,-0.25f, 0.25f);			// Left Of Triangle (Front)
		glColor3f(0.0f,0.0f,1.0f);			// Blue
		glVertex3f( 0.25f,-0.25f, 0.25f);			// Right Of Triangle (Front)

		glColor3f(1.0f,0.0f,0.0f);			// Red
		glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Right)
		glColor3f(0.0f,0.0f,1.0f);			// Blue
		glVertex3f( 0.25f,-0.25f, 0.25f);			// Left Of Triangle (Right)
		glColor3f(0.0f,1.0f,0.0f);			// Green
		glVertex3f( 0.25f,-0.25f, -0.25f);			// Right Of Triangle (Right)

		glColor3f(1.0f,0.0f,0.0f);			// Red
		glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Back)
		glColor3f(0.0f,1.0f,0.0f);			// Green
		glVertex3f( 0.25f,-0.25f, -0.25f);			// Left Of Triangle (Back)
		glColor3f(0.0f,0.0f,1.0f);			// Blue
		glVertex3f(-0.25f,-0.25f, -0.25f);			// Right Of Triangle (Back)

		glColor3f(1.0f,0.0f,0.0f);			// Red
		glVertex3f( 0.0f, 0.25f, 0.0f);			// Top Of Triangle (Left)
		glColor3f(0.0f,0.0f,1.0f);			// Blue
		glVertex3f(-0.25f,-0.25f,-0.25f);			// Left Of Triangle (Left)
		glColor3f(0.0f,1.0f,0.0f);			// Green
		glVertex3f(-0.25f,-0.25f, 0.25f);			// Right Of Triangle (Left)
	glEnd();						// Done Drawing The Pyramid
	*/
	// Draw to the screen
	std::vector<Mesh*>::iterator start = drawList.begin();
	std::vector<Mesh*>::iterator end = drawList.end();
	for(std::vector<Mesh*>::iterator itr = start; itr != end; ++itr) {
		// Position The Cubes On The Screen
		glLoadIdentity();
		//glTranslatef(-1.5f,0.0f,-6.0f);
		//glTranslatef(-1.5f,0.0f,-6.0f);	
		glTranslatef((GLfloat) (*itr)->screenCoords.x, (*itr)->screenCoords.y, (*itr)->screenCoords.z);
		
		if((*itr)->type == PYRAMID)
			glBegin(GL_TRIANGLES);
		else
			glBegin(GL_QUADS);

		// Draw the mesh
		for(int i = 0; i < (*itr)->vertices.size(); i++) {
			Vector3 color = (*itr)->colors[i];
			Vector3 position = (*itr)->vertices[i];
			glColor3f(color.x, color.y, color.z);
			glVertex3f(position.x, position.y, position.z);
		}

		glEnd();
	}

	SwapBuffers(_window->getHDC());
}

/**************************************************************************************************
* Add an object to the scene
**************************************************************************************************/
void RenderManager::addToScene(Mesh* obj) {
	drawList.push_back(obj);
}

/**************************************************************************************************
* Remove an object from the scene
**************************************************************************************************/
void RenderManager::removeFromScene(GLuint id) {
}

/**************************************************************************************************
* Build a bitmap font
**************************************************************************************************/
void RenderManager::buildFont(void) {
	HFONT font;		// windows font
	HFONT oldfont;	// for "good housekeeping"

	base = glGenLists(96);

	// create the font based on the character height, not the cell height
	font = CreateFont(-24, 0, 0, 0, FW_BOLD, false, false, false, ANSI_CHARSET, OUT_TT_PRECIS, 
		CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, FF_DONTCARE|DEFAULT_PITCH, (LPCWSTR)"Courier New");

	// set the new font by replacing the old
	oldfont = (HFONT)SelectObject(_window->getHDC(), font);
	wglUseFontBitmaps(_window->getHDC(), 32, 96, base);
	SelectObject(_window->getHDC(), oldfont);
	DeleteObject(font);
}

/**************************************************************************************************
* Destroy the font
**************************************************************************************************/
void RenderManager::killFont(void) {
	glDeleteLists(base, 96);		// delete all 96 characters
}

/**************************************************************************************************
* Prints whatever comes into the method
**************************************************************************************************/
void RenderManager::print(const char* toPrint) {
	char text[256];		// holds the string to be printed
	va_list ap;			// pointer to a list of arguments

	if(toPrint == NULL) return;		// there's nothing to print

	// check to see if there are any symbols that need to be converted and convert them
	va_start(ap, toPrint);
		vsprintf(text, toPrint, ap);
	va_end(ap);

	glPushAttrib(GL_LIST_BIT);		// prevents from affecting other display lists
	glListBase(base - 32);			// sets the base character to 32

	glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);	// draws the display list text
	glPopAttrib();	// pops the bits of the display list
}

/**************************************************************************************************
* Aligns and prints out the score
**************************************************************************************************/
void RenderManager::createScoreToPrint(int player, int ai) {
	// create the string to print
	char pScore[80]; char aScore[80];
	itoa(player, pScore, 10);
	itoa(ai, aScore, 10);
	strcpy(scoreString, pScore);
	strcat(scoreString, "     ");
	strcat(scoreString, aScore);
}